這是「Modern Blog 30 天」系列第 8 篇文章,上一篇我們實作了 Markdown 格式的文章內頁,這篇我們會擴充它,讓他支援 MDX 格式,能在 Markdown 內插入任何 React 元件,提升文章內容靈活度!
結果截圖如下:
這篇修改的程式碼如下:
我的個人網站裡也有此系列的好讀版,程式碼更易讀、也支援深色模式和側邊目錄,歡迎前往閱讀!
修改 filePathPattern 檔名,以及新增 contentType:
import { defineDocumentType, makeSource } from './src/lib/contentLayerAdapter';
export const Post = defineDocumentType(() => ({
name: 'Post',
// 更新 filePathPattern,從 *.md 改成 *.mdx,
filePathPattern: `content/posts/**/*.mdx`,
// 並新增下面這行 contentType
contentType: 'mdx',
fields: {
title: {
type: 'string',
required: true,
},
description: {
type: 'string',
required: true,
},
slug: {
type: 'string',
required: true,
},
date: {
type: 'date',
required: true,
},
},
computedFields: {
path: {
type: 'string',
resolve: (post) => `/posts/${post.slug}`,
},
},
}));
export default makeSource({
contentDirPath: 'content',
documentTypes: [Post],
});
替換掉 render html 的 div,改成 render <MDXContent />
:
import { format, parseISO } from 'date-fns';
import type { GetStaticPaths, GetStaticProps, NextPage } from 'next';
import Head from 'next/head';
// 新增下面這行
import { useMDXComponent } from 'next-contentlayer/hooks';
import { allPosts, Post } from '@/lib/contentLayerAdapter';
import styles from '@/styles/Home.module.css';
export const getStaticPaths: GetStaticPaths = () => {
const paths = allPosts.map((post) => post.path);
return {
paths,
fallback: false,
};
};
export const getStaticProps: GetStaticProps<Props> = ({ params }) => {
const post = allPosts.find((post) => post.slug === params?.slug);
if (!post) {
return {
notFound: true,
};
}
return {
props: {
post,
},
};
};
type Props = {
post: Post;
};
const PostPage: NextPage<Props> = ({ post }) => {
// 以及新增下面這行
const MDXContent = useMDXComponent(post.body.code);
return (
<div className={styles.container}>
<Head>
<title>{post.title}</title>
<meta name="description" content={post.description} />
<link rel="icon" href="/favicon.ico" />
</Head>
<main className={styles.main}>
<h1 className={styles.title}>{post.title}</h1>
<time dateTime={post.date}>
{format(parseISO(post.date), 'LLLL d, yyyy')}
</time>
// 替換掉 render html 的 div,改為 <MDXContent />
// <div dangerouslySetInnerHTML={{ __html: post.body.html }} />
<MDXContent />
</main>
</div>
);
};
export default PostPage;
將所有 content/posts/
底下的文章副檔名改成 .mdx
如此一來就完成 MDX 格式支援了!
接著讓我們來測試將 React 元件放進 MDX 文章的 Markdown 內容之間。
src/components/CustomInput.tsx
const CustomInput = () => {
return (
<div>
<p>Hello</p>
<input type="text" />
</div>
);
};
export default CustomInput;
content/posts/20220831-markdown-demo.mdx
插入剛剛的 CustomInput 元件:
---
title: Markdown demo
description: This is a demo of Markdown
slug: markdown-demo
date: 2022-08-31
type: Post
---
import CustomInput from '../../src/components/CustomInput';
## H2 title
### H3 title
Some content with [link](https://www.google.com)
<CustomInput />
完成了!使用 pnpm dev
並進入有客製化 MDX 元件的文章,就會看到 MDX 元件顯示在畫面上了。
網址會長得像這樣:
http://localhost:3000/posts/markdown-demo
截圖如下,可以看到多了一個用 React 做成的文字輸入框:
這篇修改的程式碼如下:
恭喜你!目前為止我們也能用 MDX 格式撰寫文章了!能在任意地方插入任何 React 做得出來的客製化元件,讓文章內容的靈活度更好!
但目前整個 Blog 還挺醜的,接下來讓我們著手開始美化樣式吧!
我們會使用到 Tailwind CSS 這套最近很熱門的 CSS Framework,下一篇讓我們來安裝並使用它!